package com.xuecheng.auth.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;

import javax.annotation.Resource;

/**
 * @description 授权服务器配置
 */
//在 Spring Security 5.x 之后，官方已不推荐使用 @EnableAuthorizationServer，
// 而是建议使用 Spring Security OAuth2 Authorization Server 项目（独立的授权服务器实现）。但在传统的 Spring Security OAuth2 架构中，这个注解是实现授权服务器的关键。
@Configuration
@EnableAuthorizationServer//开启了授权服务器功能, Spring Security OAuth2 中开启授权服务器功能的开关
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {

    @Resource(name = "authorizationServerTokenServicesCustom")//来自tokenConfig
    private AuthorizationServerTokenServices authorizationServerTokenServices;

    // @Autowired//无法自动装配。找不到'AuthenticationManager'类型的 Bean。
    // Spring Security 在默认情况下不会自动将AuthenticationManager注册为 Bean，需要通过显式配置来暴露它
    @Resource//来自WebSecurityConfig
    private AuthenticationManager authenticationManager;

    //客户端详情配置 定义哪些客户端（第三方应用）可以访问授权服务器，以及它们的访问规则
    @Override
    public void configure(ClientDetailsServiceConfigurer clients)throws Exception {
        clients.inMemory()// 使用内存存储客户端信息
                .withClient("XcWebApp")// client_id 客户端ID
//                .secret("XcWebApp")//客户端密钥 明文
                .secret(new BCryptPasswordEncoder().encode("XcWebApp"))//客户端密钥
                .resourceIds("xuecheng-plus")//资源列表
//  "authorization_code" 申请令牌,基于授权码形式
                .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")// 该client允许的授权类型authorization_code,password,refresh_token,implicit,client_credentials
                .scopes("all")// 允许的授权范围
                .autoApprove(false)//false跳转到授权页面
                //客户端接收授权码的重定向地址
                .redirectUris("http://www.51xuecheng.cn")
        ;
    }


    //令牌端点的访问配置
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.authenticationManager(authenticationManager)//认证管理器
                .tokenServices(authorizationServerTokenServices)//令牌管理服务 在tokenConfig中注入
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);// 限制令牌端点仅支持POST请求
    }

    //令牌端点的安全配置
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
// /oauth/token_key 端点用于获取令牌的公钥,该端点对所有请求开放（无需认证即可访问）。这是合理的，因为公钥本身是公开信息，资源服务器需要无阻碍地获取它来验证令牌。
        security.tokenKeyAccess("permitAll()")
// /oauth/check_token 端点用于验证令牌的有效性（如令牌是否过期、是否被篡改等），资源服务器在收到客户端的令牌后，通常会调用此端点校验令牌。
//permitAll() 表示该端点对所有请求开放。在实际应用中，也可以根据需求改为 isAuthenticated()（仅允许已认证的请求访问），增强安全性，但需要资源服务器先进行客户端认证。
                .checkTokenAccess("permitAll()")
//   默认情况下，客户端需通过 HTTP Basic 认证方式提交这些信息（在请求头中携带）
// 此配置开启后，客户端可以通过表单参数（application/x-www-form-urlencoded）提交 client_id 和 client_secret，简化了客户端的集成（尤其是前端应用）。
                .allowFormAuthenticationForClients();//表单认证（申请令牌）
    }
}
